#include "cli.hpp"
#include "CLI11.hpp"
#include "cliFunctions.hpp"
#include "util.hpp"
#include "formatHelpers.hpp"
#include "version.hpp"

#include "magic_enum.hpp"
#include <string>

namespace hms
{

template<bool excludeLast = false, typename EnumType>
void enumCastOrReport(
	std::optional<EnumType>& opt,
	const std::string& optStr,
	std::string_view msgBefore,
	std::string_view msgAfter = ""
){
	opt = magic_enum::enum_cast<EnumType>(optStr);
	if ( !opt.has_value() ){
		std::cerr << fmt::format(
			"{} \"{}\" {}"
			"\nValid options are:{}",
			// "\nOption is ignored.",
			msgBefore,
			bold(red("{}", optStr)),
			msgAfter,
			enumValuesToString<EnumType, excludeLast>()
		) << '\n';
	}
}

std::optional<int> parseArgs(
	const fs::path& wd, Settings& s, int argc, char* argv[]
){

	CLI::App app {
		"hms++ is a Godunov-type Shallow Water Equations solver with a focus "
		"on performance."
	};

	// ProcessedArgs pa;
	
	std::string caseDirStr;
	app.add_option(
		"dir,-d,--directory", caseDirStr,
		"The directory containing the case files"
	)->check(CLI::ExistingDirectory);

	app.add_flag(
		"-v,--version", s.printVersion,
		"Print the application version and exit"
	);

	std::string logLevelStr;
	app.add_option(
		"-l,--log-level", logLevelStr,
		"Set the log level for console output.\n"
		"Overrides ini setting, but may be overridden\n"
		"by using option \"--quiet\"."
	);

	app.add_flag(
		"-q,--quiet", s.quiet, "Execute without output to console or log file"
	);

	app.add_flag(
		"-c,--clear", s.clearFiles, "Clear output files and exit"
	);

	bool meshOnly {false};
	CLI::Option* m = app.add_flag(
		"-m,--mesh", meshOnly, "Import or generate mesh, then exit"
	);

	std::string importStr {"none"};
	app.add_option(
		"-i,--import", importStr,
		"Specify which type of data to import. Options are:" +
		enumValuesToString<Settings::ImportItem, false>()
	)->excludes(m)->expected(0,1); // allow no arguments

	app.add_flag(
		"-n,--dry-run", s.dryRun,
		"Read ini and import files to check for errors.\n"
		"No files are written, and no simulation is run."
	);

	CLI11_PARSE(app, argc, argv);

	if (s.printVersion) {
		fmt::print( "hms {}\n", hms::version::string() );
		return 0;
	}

	s.path( Settings::Path::caseDir, hms::subdirOrAbsolute(wd, caseDirStr) );

	if ( !logLevelStr.empty() ){
		enumCastOrReport<true>(s.logLevel, logLevelStr, "Unknown log level");
		if ( !s.logLevel )
			return 1;
	}

	/* if option -i was given, but no argument passed, interpret it as a flag,
	 * meaning "all" */
	if ( importStr.empty() )
		importStr = "all";
	if ( importStr != "none" ){
		enumCastOrReport( s.importItem, importStr,
			"Unknown argument", "for option --import"
		);
		if ( !s.importItem )
			return 1;
	}

	if (meshOnly)
		s.importItem = Settings::ImportItem::mesh;

	return std::nullopt;
}

} // namespace hms
